feat: implement initial repository aggregator with TypeScript and modular architecture#45
feat: implement initial repository aggregator with TypeScript and modular architecture#45lidiaAsamnew wants to merge 3 commits intoAOSSIE-Org:mainfrom
Conversation
WalkthroughReplaces static UI with a data-driven dashboard: App fetches GitHub org repositories on mount, manages loading/error state, aggregates and sorts results, and renders a grid of RepositoryCard components; adds Repository type and UI string constants. Changes
Sequence DiagramsequenceDiagram
participant App as App Component
participant GH as GitHub API
participant State as App State
participant Card as RepositoryCard
App->>App: useEffect on mount
App->>GH: Fetch /orgs/{org}/repos (paginated, parallel per org)
GH-->>App: Per-org repo pages (fulfilled/rejected)
App->>State: Aggregate fulfilled results, flatten, sort by stargazers_count
State-->>App: Update repos, set loading=false
alt No successful repos
App->>App: Set error state / render error
else Some repos fetched
App->>Card: Render RepositoryCard grid for each repo
Card-->>App: Display repo details (link, desc, stars, forks, language)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/App.tsx`:
- Around line 47-57: The component App currently hardcodes user-facing strings
("Loading AOSSIE Ecosystem...", the error message rendered when error is truthy,
"AOSSIE Org Explorer" title and the subtitle using repos.length) — extract these
literals into your i18n resources and replace inline strings with calls to the
i18n lookup (e.g., t('loading'), t('error', { message: error }), t('title'),
t('subtitle', { count: repos.length })) inside the App component; update imports
to include the i18n hook/function you use (e.g., useTranslation or t) and ensure
pluralization/placeholder usage for repos.length is defined in the resource
files.
- Around line 17-27: fetchOrgRepos lost the GITHUB_TOKEN auth header causing
unauthenticated requests; update fetchOrgRepos to include the Authorization
header (using the module-scope GITHUB_TOKEN or accept it as a parameter) so each
fetch uses headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } :
{}, and keep the pagination + Promise.allSettled approach (references:
fetchOrgRepos, GITHUB_TOKEN, ORGS, Promise.allSettled).
- Around line 6-20: The client currently exposes GITHUB_TOKEN and performs org
repo fetches inside fetchAllRepos using per_page=100 and Promise.all; move
authentication and all GitHub API calls to a server-side endpoint (e.g.,
/api/github/orgs) so the PAT is only used on the server (remove
import.meta.env.VITE_GITHUB_TOKEN usage from App), implement pagination on the
server (follow Link header or iterate pages rather than relying on per_page=100)
to return complete repo lists for ORGS, change the client-side fetchAllRepos to
call that server endpoint and handle partial failures by using
Promise.allSettled or by returning per-org status from the server and updating
setError/setRepos accordingly, and extract all user-facing strings currently
hardcoded in App (e.g., loading/error messages) into i18n resource files.
In `@src/components/RepositoryCard.tsx`:
- Around line 28-37: The RepositoryCard component currently uses inline
user-visible strings for fallbacks ("No description provided." and "Docs");
replace these literals with i18n resource lookups instead (e.g., use your
translation function like t('repo.noDescription') and t('repo.language.docs') or
pull from the i18n resource object) so repo.description and repo.language
fallbacks are externalized; update RepositoryCard to import/use the project's
i18n helper and reference keys for the two messages rather than hard-coded
strings.
In `@src/types/github.ts`:
- Around line 4-8: The type declaration currently marks the properties
description and language as string but GitHub can return null; update the
interface/type that declares description and language so both are typed as
string | null (e.g., change description: string; → description: string | null;
and language: string; → language: string | null;) to match the API and avoid
unsafe assumptions in consuming code like RepositoryCard.tsx.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 032ac4e8-8fd4-47f7-91e8-9141cc795576
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (3)
src/App.tsxsrc/components/RepositoryCard.tsxsrc/types/github.ts
src/App.tsx
Outdated
| const GITHUB_TOKEN = import.meta.env.VITE_GITHUB_TOKEN; | ||
|
|
||
| function App() { | ||
| const [repos, setRepos] = useState<Repository[]>([]); | ||
| const [loading, setLoading] = useState<boolean>(true); | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| const fetchAllRepos = async () => { | ||
| try { | ||
| setLoading(true); | ||
| const requests = ORGS.map(org => | ||
| fetch(`https://api.github.com/orgs/${org}/repos?per_page=100`, { | ||
| headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {} | ||
| }).then(res => { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -C2 --type ts --type tsx 'VITE_GITHUB_TOKEN|Authorization:\s*`token\s+\$\{GITHUB_TOKEN\}`|import\.meta\.env\.VITE_'Repository: AOSSIE-Org/OrgExplorer
Length of output: 93
🏁 Script executed:
#!/bin/bash
# Find and read the App.tsx file
echo "=== Checking if App.tsx exists ==="
ls -la src/App.tsx 2>&1 || echo "File not found"
echo ""
echo "=== Reading src/App.tsx ==="
cat -n src/App.tsx | head -50Repository: AOSSIE-Org/OrgExplorer
Length of output: 2176
🏁 Script executed:
#!/bin/bash
# Search for VITE_GITHUB_TOKEN and Authorization header patterns
echo "=== Searching for VITE_GITHUB_TOKEN ==="
rg -n 'VITE_GITHUB_TOKEN' . 2>&1
echo ""
echo "=== Searching for Authorization header with token ==="
rg -n 'Authorization.*token.*GITHUB_TOKEN' . 2>&1
echo ""
echo "=== Searching for import.meta.env.VITE usage ==="
rg -n 'import\.meta\.env\.VITE_' . 2>&1Repository: AOSSIE-Org/OrgExplorer
Length of output: 455
🏁 Script executed:
#!/bin/bash
# Check for i18n setup and usage
echo "=== Looking for i18n configuration ==="
find src -name '*i18n*' -o -name '*translation*' -o -name '*locale*' 2>&1 | head -20
echo ""
echo "=== Searching for potential user-visible strings in App.tsx ==="
rg -n '(loading|error|Loading|Error|fetching|Fetching)' src/App.tsx 2>&1Repository: AOSSIE-Org/OrgExplorer
Length of output: 753
Move GitHub authentication to a server-side endpoint—do not send tokens from client code.
Lines 6 and 19 expose a GitHub PAT to the browser. Vite's VITE_ prefixed environment variables are automatically embedded in the client-side bundle, making the token visible to users. Authentication must happen server-side with credentials kept secure.
Additionally:
- Pagination: Line 18 uses
per_page=100which truncates results for organizations with more than 100 repositories. - Reliability: Line 26 uses
Promise.all(), which fails all requests if any single organization request fails. ConsiderPromise.allSettled()to handle individual org failures gracefully. - Internationalization: User-visible strings at lines 35, 47, and 51 must be externalized to resource files per i18n guidelines (currently hardcoded English strings).
Example server-side endpoint pattern
Move fetch logic to a backend route (e.g., /api/github/orgs) that holds the token server-side, and call that from the client instead.
-const GITHUB_TOKEN = import.meta.env.VITE_GITHUB_TOKEN;
...
- fetch(`https://api.github.com/orgs/${org}/repos?per_page=100`, {
- headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {}
- }).then(res => {
+ fetch(`/api/github/orgs/${org}/repos`).then(res => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/App.tsx` around lines 6 - 20, The client currently exposes GITHUB_TOKEN
and performs org repo fetches inside fetchAllRepos using per_page=100 and
Promise.all; move authentication and all GitHub API calls to a server-side
endpoint (e.g., /api/github/orgs) so the PAT is only used on the server (remove
import.meta.env.VITE_GITHUB_TOKEN usage from App), implement pagination on the
server (follow Link header or iterate pages rather than relying on per_page=100)
to return complete repo lists for ORGS, change the client-side fetchAllRepos to
call that server endpoint and handle partial failures by using
Promise.allSettled or by returning per-org status from the server and updating
setError/setRepos accordingly, and extract all user-facing strings currently
hardcoded in App (e.g., loading/error messages) into i18n resource files.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
src/App.tsx (2)
8-15:⚠️ Potential issue | 🔴 CriticalThe PAT is still public in a Vite build.
The note at Lines 9-12 is not accurate for
VITE_variables: they are compiled into browser-accessible code. If this must stay backend-less, collect the PAT at runtime and keep it in memory instead ofimport.meta.env.In Vite, are environment variables prefixed with `VITE_` embedded into client-side bundles and accessible from browser code?As per coding guidelines, "No exposed API keys or sensitive data".
Also applies to: 26-27
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/App.tsx` around lines 8 - 15, The GITHUB_TOKEN constant uses import.meta.env.VITE_GITHUB_TOKEN which Vite embeds into the client bundle making the PAT public; change to collecting the PAT at runtime (e.g., prompt or input component) and store it only in-memory (React state or a context) instead of using import.meta.env, replacing references to GITHUB_TOKEN with the runtime state variable and removing import.meta.env.VITE_GITHUB_TOKEN usage in App.tsx.
22-30:⚠️ Potential issue | 🟠 Major
fetchOrgRepos()still stops at page 1.
pageis interpolated into the URL, but the function never advances it, so any org above 100 repos is truncated. Loop until a page returns fewer than 100 items, or follow theLinkheader.📄 Suggested pagination fix
- const fetchOrgRepos = async (org: string, page = 1): Promise<Repository[]> => { - const response = await fetch( - `https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`, - { - headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {}, - } - ); - if (!response.ok) throw new Error(`${response.status} while fetching ${org}`); - return response.json(); - }; + const fetchOrgRepos = async (org: string): Promise<Repository[]> => { + const allRepos: Repository[] = []; + for (let page = 1; ; page += 1) { + const response = await fetch( + `https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`, + { + headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {}, + } + ); + if (!response.ok) throw new Error(`${response.status} while fetching ${org}`); + + const pageRepos: Repository[] = await response.json(); + allRepos.push(...pageRepos); + if (pageRepos.length < 100) break; + } + return allRepos; + };For GitHub REST `GET /orgs/{org}/repos`, is `per_page` capped at 100 and do clients need to paginate with `page` or the `Link` header to retrieve all repositories?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/App.tsx` around lines 22 - 30, fetchOrgRepos only fetches a single page because it never advances or loops over the page parameter; update fetchOrgRepos to paginate (either by following the Link header or by looping pages incrementing page) and accumulate results across requests until no more repos are returned (response length < 100 or Link header has no "next"). Keep the existing signature (fetchOrgRepos) but perform repeated fetches with the same Authorization header, push each page's JSON into a growing array, and return the combined Repository[]; ensure errors still throw on non-ok responses and handle Link parsing if you choose that approach.src/constants/strings.ts (1)
1-10:⚠️ Potential issue | 🟠 MajorThis is still not i18n-ready.
The new constants file centralizes copy, but it still hardcodes English, and
ERROR_PREFIX/SUBTITLE_*split sentences in a way translators cannot reorder or pluralize. Use full locale keys with parameters instead of prefix/suffix fragments.As per coding guidelines, "Internationalization: User-visible strings should be externalized to resource files (i18n)".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/constants/strings.ts` around lines 1 - 10, UI_STRINGS currently hardcodes English and splits sentences into fragments (ERROR_PREFIX, SUBTITLE_PREFIX, SUBTITLE_SUFFIX) which prevents proper i18n and pluralization; replace these fragmented keys with full, parameterized locale keys (e.g., ERROR_MESSAGE with a {message} param, SUBTITLE with a {count} param and possible plural forms) and move all user-facing strings out of src/constants/strings.ts into the app's i18n resource files (locale JSONs) so callers use the i18n lookup (e.g., t('ERROR_MESSAGE', { message }) or t('SUBTITLE', { count })) instead of concatenating prefixes/suffixes or using hardcoded English in UI_STRINGS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/App.tsx`:
- Around line 37-49: The current logic drops rejected org fetches and only sets
error when all orgs fail; modify the fetch flow that uses ORGS and fetchOrgRepos
so each promise is paired with its org name (e.g., map ORGS to {org, promise})
and after Promise.allSettled inspect results to build two things: (1)
successfulRepos (as you already do) and call setRepos(successfulRepos), and (2)
a list of failedOrgs derived from rejected results; introduce or reuse a state
like setWarning/setPartialFetchWarning and set a non-blocking warning message
describing failedOrgs (instead of only calling setError when
successfulRepos.length === 0), so partial results are shown while failed orgs
are surfaced to the UI.
---
Duplicate comments:
In `@src/App.tsx`:
- Around line 8-15: The GITHUB_TOKEN constant uses
import.meta.env.VITE_GITHUB_TOKEN which Vite embeds into the client bundle
making the PAT public; change to collecting the PAT at runtime (e.g., prompt or
input component) and store it only in-memory (React state or a context) instead
of using import.meta.env, replacing references to GITHUB_TOKEN with the runtime
state variable and removing import.meta.env.VITE_GITHUB_TOKEN usage in App.tsx.
- Around line 22-30: fetchOrgRepos only fetches a single page because it never
advances or loops over the page parameter; update fetchOrgRepos to paginate
(either by following the Link header or by looping pages incrementing page) and
accumulate results across requests until no more repos are returned (response
length < 100 or Link header has no "next"). Keep the existing signature
(fetchOrgRepos) but perform repeated fetches with the same Authorization header,
push each page's JSON into a growing array, and return the combined
Repository[]; ensure errors still throw on non-ok responses and handle Link
parsing if you choose that approach.
In `@src/constants/strings.ts`:
- Around line 1-10: UI_STRINGS currently hardcodes English and splits sentences
into fragments (ERROR_PREFIX, SUBTITLE_PREFIX, SUBTITLE_SUFFIX) which prevents
proper i18n and pluralization; replace these fragmented keys with full,
parameterized locale keys (e.g., ERROR_MESSAGE with a {message} param, SUBTITLE
with a {count} param and possible plural forms) and move all user-facing strings
out of src/constants/strings.ts into the app's i18n resource files (locale
JSONs) so callers use the i18n lookup (e.g., t('ERROR_MESSAGE', { message }) or
t('SUBTITLE', { count })) instead of concatenating prefixes/suffixes or using
hardcoded English in UI_STRINGS.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0d387a10-40b5-4ade-8ce9-f4a612a98667
📒 Files selected for processing (4)
src/App.tsxsrc/components/RepositoryCard.tsxsrc/constants/strings.tssrc/types/github.ts
| const results = await Promise.allSettled(ORGS.map(org => fetchOrgRepos(org))); | ||
|
|
||
| const successfulRepos = results | ||
| .filter((result): result is PromiseFulfilledResult<Repository[]> => result.status === 'fulfilled') | ||
| .map(result => result.value) | ||
| .flat() | ||
| .sort((a, b) => b.stargazers_count - a.stargazers_count); | ||
|
|
||
| setRepos(successfulRepos); | ||
|
|
||
| if (successfulRepos.length === 0) { | ||
| setError("No data could be retrieved from GitHub."); | ||
| } |
There was a problem hiding this comment.
Surface partial-org failures instead of silently dropping them.
Rejected org fetches are filtered out, but error is only set when every org fails. That lets the dashboard look complete while one or more organizations are missing. Wrap each promise with its org name, track the rejected orgs, and show a non-blocking warning alongside the partial results.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/App.tsx` around lines 37 - 49, The current logic drops rejected org
fetches and only sets error when all orgs fail; modify the fetch flow that uses
ORGS and fetchOrgRepos so each promise is paired with its org name (e.g., map
ORGS to {org, promise}) and after Promise.allSettled inspect results to build
two things: (1) successfulRepos (as you already do) and call
setRepos(successfulRepos), and (2) a list of failedOrgs derived from rejected
results; introduce or reuse a state like setWarning/setPartialFetchWarning and
set a non-blocking warning message describing failedOrgs (instead of only
calling setError when successfulRepos.length === 0), so partial results are
shown while failed orgs are surfaced to the UI.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/App.tsx (2)
61-66:⚠️ Potential issue | 🟠 MajorPartial org failures are silently dropped.
When some orgs succeed and others fail, the error state is only set if
successfulRepos.length === 0. Users see incomplete data without any indication that one or more organizations failed to load. Consider tracking rejected orgs separately and displaying a non-blocking warning alongside the partial results.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/App.tsx` around lines 61 - 66, The code only sets an error when all org requests fail, which silently hides partial failures; update the handling around results/successfulRepos to collect rejected items (e.g., filter results for status === 'rejected' and extract their reasons and associated org identifiers), then call a non-blocking warning state setter (create/use a state like setWarning or setPartialFailure) with a concise message listing failed orgs/reasons while still calling setRepos/setSuccessfulRepos for the successful responses; ensure the UI reads that warning state to display a warning alongside the partial results.
9-9:⚠️ Potential issue | 🔴 CriticalSecurity: GitHub PAT is exposed to the browser.
VITE_prefixed environment variables are embedded in the client-side bundle, making the token visible to users via browser dev tools or source inspection. This authentication should be moved to a server-side endpoint.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/App.tsx` at line 9, The code exposes a GitHub PAT by using import.meta.env.VITE_GITHUB_TOKEN (GITHUB_TOKEN constant) in the client; remove any direct usage of GITHUB_TOKEN/import.meta.env.VITE_GITHUB_TOKEN from the frontend and instead create a server-side endpoint that reads the secret from a non-VITE_ env var and performs authenticated GitHub requests (or proxies requests) on behalf of the client; update the frontend to call that endpoint (e.g., fetch /api/github/...) and handle responses, and ensure the secret is stored only in server env vars and not bundled into the client.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/App.tsx`:
- Around line 16-75: The effect should use an AbortController to cancel
in-flight fetches on unmount: create const controller = new AbortController() in
the useEffect, pass controller.signal into fetchOrgRepos (add a signal param to
fetchOrgRepos and include it in the fetch options), and return a cleanup
function that calls controller.abort(). Inside fetchOrgRepos and fetchAllData,
handle aborts by checking controller.signal.aborted (or catching an error with
name === 'AbortError') and bail out early so you don't call setLoading,
setRepos, or setError after unmount; also, before applying results after
Promise.allSettled in fetchAllData, check controller.signal.aborted and return
if true. Ensure references: fetchOrgRepos, fetchAllData, ORGS, setLoading,
setRepos, setError, and useEffect are updated accordingly.
---
Duplicate comments:
In `@src/App.tsx`:
- Around line 61-66: The code only sets an error when all org requests fail,
which silently hides partial failures; update the handling around
results/successfulRepos to collect rejected items (e.g., filter results for
status === 'rejected' and extract their reasons and associated org identifiers),
then call a non-blocking warning state setter (create/use a state like
setWarning or setPartialFailure) with a concise message listing failed
orgs/reasons while still calling setRepos/setSuccessfulRepos for the successful
responses; ensure the UI reads that warning state to display a warning alongside
the partial results.
- Line 9: The code exposes a GitHub PAT by using
import.meta.env.VITE_GITHUB_TOKEN (GITHUB_TOKEN constant) in the client; remove
any direct usage of GITHUB_TOKEN/import.meta.env.VITE_GITHUB_TOKEN from the
frontend and instead create a server-side endpoint that reads the secret from a
non-VITE_ env var and performs authenticated GitHub requests (or proxies
requests) on behalf of the client; update the frontend to call that endpoint
(e.g., fetch /api/github/...) and handle responses, and ensure the secret is
stored only in server env vars and not bundled into the client.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b02287cf-f2d8-4c8e-b737-d6b15358d8cc
📒 Files selected for processing (2)
src/App.tsxsrc/types/github.ts
| useEffect(() => { | ||
|
|
||
| const fetchOrgRepos = async (org: string): Promise<Repository[]> => { | ||
| let allOrgRepos: Repository[] = []; | ||
| let page = 1; | ||
| let hasNextPage = true; | ||
|
|
||
| while (hasNextPage) { | ||
| const response = await fetch( | ||
| `https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`, | ||
| { | ||
| headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {}, | ||
| } | ||
| ); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`${response.status} while fetching ${org}`); | ||
| } | ||
|
|
||
| const data: Repository[] = await response.json(); | ||
| allOrgRepos = [...allOrgRepos, ...data]; | ||
|
|
||
| if (data.length === 100) { | ||
| page++; | ||
| } else { | ||
| hasNextPage = false; | ||
| } | ||
| } | ||
| return allOrgRepos; | ||
| }; | ||
|
|
||
| const fetchAllData = async () => { | ||
| try { | ||
| setLoading(true); | ||
|
|
||
| const results = await Promise.allSettled(ORGS.map(org => fetchOrgRepos(org))); | ||
|
|
||
| const successfulRepos = results | ||
| .filter((result): result is PromiseFulfilledResult<Repository[]> => result.status === 'fulfilled') | ||
| .map(result => result.value) | ||
| .flat() | ||
| .sort((a, b) => b.stargazers_count - a.stargazers_count); | ||
|
|
||
| setRepos(successfulRepos); | ||
|
|
||
| if (successfulRepos.length === 0) { | ||
| const failureReasons = results | ||
| .filter((r): r is PromiseRejectedResult => r.status === 'rejected') | ||
| .map(r => r.reason.message).join(', '); | ||
| setError(`Failed to fetch data: ${failureReasons}`); | ||
| } | ||
| } catch (err) { | ||
| setError(err instanceof Error ? err.message : UI_STRINGS.UNKNOWN_ERROR); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| fetchAllData(); | ||
| }, []); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding AbortController for fetch cleanup.
The useEffect lacks cleanup logic to abort pending fetch requests if the component unmounts mid-fetch. While less critical for the root App component, this is a React best practice that prevents potential memory leaks and "setState on unmounted component" warnings.
Suggested pattern
useEffect(() => {
+ const controller = new AbortController();
+
const fetchOrgRepos = async (org: string): Promise<Repository[]> => {
// ...
const response = await fetch(
`https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`,
{
headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {},
+ signal: controller.signal,
}
);
// ...
};
// ... fetchAllData ...
fetchAllData();
+
+ return () => controller.abort();
}, []);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/App.tsx` around lines 16 - 75, The effect should use an AbortController
to cancel in-flight fetches on unmount: create const controller = new
AbortController() in the useEffect, pass controller.signal into fetchOrgRepos
(add a signal param to fetchOrgRepos and include it in the fetch options), and
return a cleanup function that calls controller.abort(). Inside fetchOrgRepos
and fetchAllData, handle aborts by checking controller.signal.aborted (or
catching an error with name === 'AbortError') and bail out early so you don't
call setLoading, setRepos, or setError after unmount; also, before applying
results after Promise.allSettled in fetchAllData, check
controller.signal.aborted and return if true. Ensure references: fetchOrgRepos,
fetchAllData, ORGS, setLoading, setRepos, setError, and useEffect are updated
accordingly.
Description
I am submitting this initial PR to demonstrate my technical approach and architectural vision for the Org Explorer as a GSoC 2026 candidate. This PR implements the foundational data-fetching architecture and repository aggregator for the 3 AOSSIE organizations.
Technical Highlights:
Promise.allfor AOSSIE-Org, StabilityNexus, and DjedAlliance.RepositoryCardcomponent following React best practices..envand provided.env.exampletemplate.Addressed Issues:
Fixes #30
Screenshots/Recordings:
Additional Notes:
Verified locally with a Personal Access Token, successfully aggregating 169+ repositories. This serves as the visualization foundation for the Mentor Dashboard.
Checklist
I have utilized AI tools to assist in structural design and documentation. I have personally reviewed and verified all code changes.
Summary by CodeRabbit
New Features
Bug Fixes / Resilience